package com.milepost.workflow.flowinst.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.milepost.workflow.attach.entity.FlowAttachEntity;
import com.milepost.workflow.attach.mapper.FlowAttachMapper;
import com.milepost.workflow.common.dto.BootstrapTableVoDto;
import com.milepost.workflow.common.enums.PassEnum;
import com.milepost.workflow.common.enums.TaskTypeEnum;
import com.milepost.workflow.flowdef.entity.FlowDefEntity;
import com.milepost.workflow.flowdef.mapper.FlowDefMapper;
import com.milepost.workflow.flowdefform.entity.FlowDefFormEntity;
import com.milepost.workflow.flowdefform.mapper.FlowDefFormMapper;
import com.milepost.workflow.flowdeftask.entity.FlowDefTaskEntity;
import com.milepost.workflow.flowdeftask.mapper.FlowDefTaskMapper;
import com.milepost.workflow.flowinst.dto.StartFlowDto;
import com.milepost.workflow.flowinst.entity.FlowInstEntity;
import com.milepost.workflow.flowinst.mapper.FlowInstMapper;
import com.milepost.workflow.flowinst.service.FlowInstSeqService;
import com.milepost.workflow.flowinst.service.FlowInstService;
import com.milepost.workflow.flowinst.vo.FlowInstVo;
import com.milepost.workflow.flowinstform.entity.FlowInstFormEntity;
import com.milepost.workflow.flowinstform.mapper.FlowInstFormMapper;
import com.milepost.workflow.flowinsttask.entity.FlowInstTaskEntity;
import com.milepost.workflow.flowinsttask.mapper.FlowInstTaskMapper;
import com.milepost.workflow.flowinsttaskapp.entity.FlowInstTaskAppEntity;
import com.milepost.workflow.flowinsttaskapp.mapper.FlowInstTaskAppMapper;
import com.milepost.workflow.user.service.UserService;
import com.milepost.workflow.util.NamingFormatUtil;
import com.milepost.workflow.util.UUIDUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author huarf
 * @Description
 * @create 2022-03-27 16:54
 */
@Service
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class FlowInstServiceImpl extends ServiceImpl<FlowInstMapper, FlowInstEntity> implements FlowInstService {

    private final FlowDefMapper flowDefMapper;

    private final FlowDefTaskMapper flowDefTaskMapper;

    private final FlowDefFormMapper flowDefFormMapper;

    private final FlowInstMapper flowInstMapper;

    private final FlowInstTaskMapper flowInstTaskMapper;

    private final FlowInstFormMapper flowInstFormMapper;

    private final UserService userService;

    private final FlowInstTaskAppMapper flowInstTaskAppMapper;

    private final FlowInstSeqService flowInstSeqService;

    private final FlowAttachMapper flowAttachMapper;

    /**
     * 发起流程
     * 1、复制流程定义数据，
     * 2、复制流程定义节点数据
     * 3、复制流程定义表单数据、保存流程表单数据
     * <p>
     * 一个流程定义可以被不同人发起多次，所以这里不需要任何同步
     *
     * @param startFlowDto
     * @return
     */
    @Override
    public String startFlow(StartFlowDto startFlowDto) {

        FlowInstEntity flowInstEntity = copyFlowDef(startFlowDto);
        if (flowInstEntity == null) {
            throw new RuntimeException("复制流程定义数据失败");
        }

        boolean success = copyFlowDefTask(flowInstEntity, startFlowDto);
        if (!success) {
            throw new RuntimeException("复制流程定义节点数据失败");
        }

        success = copyFlowDefForm(flowInstEntity, startFlowDto);
        if (!success) {
            throw new RuntimeException("复制流程定义表单数据失败");
        }

        success = setCurrTaskIdForFlowInst(flowInstEntity);
        if (!success) {
            throw new RuntimeException("设置流程实例的当前节点值失败");
        }

        return flowInstEntity.getId();
    }

    /**
     * 设置流程实例的当前节点，
     *
     * @param flowInstEntity
     * @return
     */
    private boolean setCurrTaskIdForFlowInst(FlowInstEntity flowInstEntity) {

        String secondTaskId = getSecondTaskId(flowInstEntity.getId());
        int update = flowInstMapper.update(null, Wrappers.<FlowInstEntity>lambdaUpdate()
                .set(FlowInstEntity::getCurrTaskId, secondTaskId)
                .eq(FlowInstEntity::getId, flowInstEntity.getId()));

        return update > 0;
    }

    @Override
    public Page<FlowInstEntity> getStartFlowInstList(BootstrapTableVoDto bootstrapTableVoDto, FlowInstEntity flowInstEntity) {

        QueryWrapper<FlowInstEntity> flowInstEntityQueryWrapper = new QueryWrapper<>();

        flowInstEntityQueryWrapper.lambda().eq(FlowInstEntity::getStartUser, flowInstEntity.getStartUser());

        if (StringUtils.isNoneBlank(flowInstEntity.getSn())) {
            flowInstEntityQueryWrapper.lambda().like(FlowInstEntity::getSn, flowInstEntity.getSn());
        }

        if (StringUtils.isNoneBlank(flowInstEntity.getName())) {
            flowInstEntityQueryWrapper.lambda().like(FlowInstEntity::getName, flowInstEntity.getName());
        }

        if (StringUtils.isNoneBlank(flowInstEntity.getDes())) {
            flowInstEntityQueryWrapper.lambda().like(FlowInstEntity::getDes, flowInstEntity.getDes());
        }

        if (flowInstEntity.getPass() != null) {
            flowInstEntityQueryWrapper.lambda().like(FlowInstEntity::getPass, flowInstEntity.getPass());
        }

        if (bootstrapTableVoDto.sortAvailable()) {
            flowInstEntityQueryWrapper.orderBy(true, bootstrapTableVoDto.isAsc(),
                    NamingFormatUtil.camelhump2LowerCaseUnderline(bootstrapTableVoDto.getSortName()));
        } else {
            //默认排序方式
            flowInstEntityQueryWrapper.orderBy(true, false, "start_time");
        }

        Page<FlowInstEntity> page = new Page<>(bootstrapTableVoDto.getPageNumber(), bootstrapTableVoDto.getPageSize());
        Page<FlowInstEntity> flowInstEntityPage = baseMapper.selectPage(page, flowInstEntityQueryWrapper);

        //关联当前节点
        for (FlowInstEntity record : flowInstEntityPage.getRecords()) {
            //关联当前节点名称
            if (record.getPass() == PassEnum.WAITING.getCode()) {
                FlowInstTaskEntity flowInstTaskEntity = flowInstTaskMapper.selectById(record.getCurrTaskId());
                record.setCurrTaskName(flowInstTaskEntity.getName());
            }
        }

        return flowInstEntityPage;
    }

    @Override
    public FlowInstVo getFlowInstById(String id) {
        FlowInstVo flowInstVo = new FlowInstVo();

        FlowInstEntity flowInstEntity = baseMapper.selectById(id);

        //查询任务
        List<FlowInstTaskEntity> flowInstTaskEntities = flowInstTaskMapper.selectList(new QueryWrapper<FlowInstTaskEntity>().lambda()
                .eq(FlowInstTaskEntity::getFlowInstId, id)
                .orderByAsc(FlowInstTaskEntity::getOrderNum));

        //关联任务审批记录
        for (FlowInstTaskEntity flowInstTaskEntity : flowInstTaskEntities) {
            if (flowInstTaskEntity.getType().equalsIgnoreCase(TaskTypeEnum.START.getType())) {
                //查询开始节点的附件
                List<FlowAttachEntity> startTaskFlowAttachEntityList = flowAttachMapper.selectList(new QueryWrapper<FlowAttachEntity>().lambda()
                        .eq(FlowAttachEntity::getFlowInstId, id)
                        .eq(FlowAttachEntity::getFlowInstTaskId, flowInstTaskEntity.getId())
                        .orderByAsc(FlowAttachEntity::getOrderNum));
                flowInstVo.setStartTaskFlowAttachEntityList(startTaskFlowAttachEntityList);
            }

            List<FlowInstTaskAppEntity> flowInstTaskAppEntityList = flowInstTaskAppMapper.selectList(new QueryWrapper<FlowInstTaskAppEntity>().lambda()
                    .eq(FlowInstTaskAppEntity::getFlowInstTaskId, flowInstTaskEntity.getId())
                    .orderByAsc(FlowInstTaskAppEntity::getEndTime));
            for (FlowInstTaskAppEntity flowInstTaskAppEntity : flowInstTaskAppEntityList) {
                //关联附件
                List<FlowAttachEntity> flowAttachEntityList = flowAttachMapper.selectList(new QueryWrapper<FlowAttachEntity>().lambda()
                        .eq(FlowAttachEntity::getFlowInstTaskAppId, flowInstTaskAppEntity.getId())
                        .orderByAsc(FlowAttachEntity::getOrderNum));
                flowInstTaskAppEntity.setFlowAttachEntityList(flowAttachEntityList);
            }
            flowInstTaskEntity.setFlowInstTaskAppEntityList(flowInstTaskAppEntityList);
        }

        flowInstVo.setFlowInstEntity(flowInstEntity);
        flowInstVo.setFlowInstTaskEntityList(flowInstTaskEntities);

        return flowInstVo;
    }

    /**
     * 复制流程定义表单数据、保存流程表单数据
     *
     * @param flowInstEntity
     * @param startFlowDto
     * @return
     */
    private boolean copyFlowDefForm(FlowInstEntity flowInstEntity, StartFlowDto startFlowDto) {

        FlowDefFormEntity flowDefFormEntity = flowDefFormMapper.selectOne(new QueryWrapper<FlowDefFormEntity>()
                .lambda().eq(FlowDefFormEntity::getFlowDefId, startFlowDto.getFlowDefId()));
        FlowInstFormEntity flowInstFormEntity = new FlowInstFormEntity(flowDefFormEntity);
        flowInstFormEntity.setId(UUIDUtil.randomUUID());
        flowInstFormEntity.setFlowInstId(flowInstEntity.getId());
        flowInstFormEntity.setVal(startFlowDto.getFlowFormData());
        int insert = flowInstFormMapper.insert(flowInstFormEntity);
        return insert > 0;
    }

    /**
     * 复制流程定义节点数据
     *
     * @param flowInstEntity
     * @param startFlowDto
     * @return
     */
    private boolean copyFlowDefTask(FlowInstEntity flowInstEntity, StartFlowDto startFlowDto) {
        //获取所有节点数据，
        List<FlowDefTaskEntity> flowDefTaskEntityList = getFlowDefTaskList(startFlowDto.getFlowDefId());

        String flowInstId = flowInstEntity.getId();
        Date startTime = flowInstEntity.getStartTime();
        Map<String, String> assigneesMap = startFlowDto.getAssignees();

        //逐条插入
        int total = 0;
        for (int i = 0; i < flowDefTaskEntityList.size(); i++) {
            FlowDefTaskEntity flowDefTaskEntity = flowDefTaskEntityList.get(i);
            FlowInstTaskEntity flowInstTaskEntity = new FlowInstTaskEntity(flowDefTaskEntity);
            flowInstTaskEntity.setId(UUIDUtil.randomUUID());
            flowInstTaskEntity.setFlowInstId(flowInstId);
            if (flowInstTaskEntity.getType().equalsIgnoreCase(TaskTypeEnum.POINT_TASK.getType())) {
                flowInstTaskEntity.setAssignees(assigneesMap.get(flowDefTaskEntity.getId()));
            }

            // start
            if (flowInstTaskEntity.getType().equalsIgnoreCase(TaskTypeEnum.START.getType())) {
                flowInstTaskEntity.setStartTime(startTime);
                flowInstTaskEntity.setEndTime(startTime);
                flowInstTaskEntity.setPass(PassEnum.AGREE.getCode());//通过
            }

            // 第一个任务
            if (i == 1) {
                flowInstTaskEntity.setStartTime(startTime);
                flowInstTaskEntity.setPass(PassEnum.WAITING.getCode());//已开始未完成
            }

            // 其他任务
            if (i > 1) {
                flowInstTaskEntity.setPass(PassEnum.NOT_START.getCode());//未开始
            }

            int insert = flowInstTaskMapper.insert(flowInstTaskEntity);
            total = total + insert;
        }

        return total == flowDefTaskEntityList.size();
    }

    /**
     * 获取所有流程定义节点数据
     *
     * @param flowDefId
     * @return
     */
    private List<FlowDefTaskEntity> getFlowDefTaskList(String flowDefId) {

        List<FlowDefTaskEntity> flowDefTaskEntities = flowDefTaskMapper.selectList(new QueryWrapper<FlowDefTaskEntity>().lambda()
                .eq(FlowDefTaskEntity::getFlowDefId, flowDefId)
                .orderByAsc(FlowDefTaskEntity::getOrderNum));
        return flowDefTaskEntities;
    }

    /**
     * 获取所有流程实例节点数据
     *
     * @param flowInstId
     * @return
     */
    private List<FlowInstTaskEntity> getFlowInstTaskList(String flowInstId) {

        List<FlowInstTaskEntity> flowInstTaskEntities = flowInstTaskMapper.selectList(new QueryWrapper<FlowInstTaskEntity>().lambda()
                .eq(FlowInstTaskEntity::getFlowInstId, flowInstId)
                .orderByAsc(FlowInstTaskEntity::getOrderNum));
        return flowInstTaskEntities;
    }

    /**
     * 复制流程定义数据
     *
     * @param startFlowDto
     * @return
     */
    private FlowInstEntity copyFlowDef(StartFlowDto startFlowDto) {

        FlowDefEntity flowDefEntity = flowDefMapper.selectById(startFlowDto.getFlowDefId());
        FlowInstEntity flowInstEntity = new FlowInstEntity(flowDefEntity);
        flowInstEntity.setId(UUIDUtil.randomUUID());
        flowInstEntity.setSn(flowInstSeqService.nextFlowInstSn());
        flowInstEntity.setStartUser(startFlowDto.getStartUser());
        flowInstEntity.setStartTime(new Date());
        flowInstEntity.setPass(PassEnum.WAITING.getCode());
        int insert = flowInstMapper.insert(flowInstEntity);
        if (insert > 0) {
            return flowInstEntity;
        } else {
            return null;
        }
    }

    /**
     * 获取第二个任务的id，即start之后的第一个任务的id，流程启动后即到达这个节点
     *
     * @param flowInstId
     * @return
     */
    private String getSecondTaskId(String flowInstId) {

        List<FlowInstTaskEntity> flowInstTaskList = getFlowInstTaskList(flowInstId);
        return flowInstTaskList.get(1).getId();
    }

}
